package com.auth.practice.application.controller;

import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.*;
import java.util.jar.JarEntry;

@Component
public class AnnotationScanner {
    List<String> classFileList = new ArrayList<>();
    public static final String type;

    static {
        type = "\\";
    }

    public Map<String, Object> scanner() throws Exception {
        URL resource = Objects.requireNonNull(ClassUtils.getDefaultClassLoader()).getResource("com/auth/practice/application");
        if (null == resource) {
            throw new RuntimeException("加载资源异常");
        }

        classFileList.clear();

        String property = System.getProperty("os.name");
        List<Class> classList = new ArrayList<>();
        Map<String, Object> resultMap = new HashMap<>();

        if (property.startsWith("WINDOWS")) {
            getWindowsInfo(resource, classList, resultMap);
        }
        if (property.startsWith("LINUX")) {
            getLinuxInfo(resource, classList, resultMap);
        }

        return resultMap;

    }

    private void getLinuxInfo(URL url, List<Class> classList, Map<String, Object> resultMap) throws ClassNotFoundException, IOException {
        Enumeration<JarEntry> jarEntries = ((JarURLConnection) url.openConnection())
                .getJarFile().entries();
        while (jarEntries.hasMoreElements()) {
            JarEntry jarEntry = jarEntries.nextElement();
            String jarName = jarEntry.getName();

            if (!jarEntry.isDirectory() && jarName.endsWith(".class")) {
                // 将文件路径名转换为包名称的形式
                classFileList.add(jarName);
            }
        }
        getClassPath(classList);
        getMethod(classList, resultMap);
    }


    private void getWindowsInfo(URL resource, List<Class> classList, Map<String, Object> resultMap) throws ClassNotFoundException {
        String packAgePath = resource.getFile();
        File[] files = new File(packAgePath).listFiles(pathName -> (pathName.isFile() && pathName.getName().endsWith(".class") || pathName.isDirectory()));
        for (File file : files) {
            getClassFile(file);
        }
        getClassPath(classList);
        getMethod(classList, resultMap);
    }


    private void getMethod(List<Class> classList, Map<String, Object> methodMap) throws ClassNotFoundException {
        for (Class<?> aClass : classList) {
            Method[] methods = aClass.getMethods();
            for (Method method : methods) {
                MyMethodAnnotation annotation = method.getAnnotation(MyMethodAnnotation.class);
                if (null == annotation) {
                    continue;
                }
                methodMap.put(annotation.value(), method.getName() + "->" + aClass.toString().replace("class ", ""));
            }
        }
    }

    private void getClassPath(List<Class> classList) throws ClassNotFoundException {
        for (String classPath : classFileList) {
            if (classPath.contains("/")) {
                String[] targetClassPath = classPath.split("classes/");
                Class<?> aClass = Class.forName(targetClassPath[1].replace(".class", ""));
                if (!aClass.isAnnotation() && !aClass.isInterface() && !aClass.isEnum() && !aClass.isPrimitive()) {
                    classList.add(aClass);
                }
            }

            if (classPath.contains(type)) {
                classPath = classPath.replace(type, ".");
                String[] targetClassPath = classPath.split("classes\\.");
                Class<?> aClass = Class.forName(targetClassPath[1].replace(".class", ""));
                if (!aClass.isAnnotation() && !aClass.isInterface() && !aClass.isEnum() && !aClass.isPrimitive()) {
                    classList.add(aClass);
                }
            }
        }
    }

    private void getClassFile(File file) {
        if (file.getName().endsWith(".class")) {
            classFileList.add(file.getName());
        } else {
            File[] files = file.listFiles(pathName -> (pathName.isFile() && pathName.getName().endsWith(".class") || pathName.isDirectory()));
            for (File subFile : files) {
                if (!subFile.getName().endsWith(".class")) {
                    getClassFile(subFile);
                } else {
                    classFileList.add(subFile.getName());
                }
            }
        }
    }
}
